﻿using System;
using System.Collections.Generic;
using System.Text;
using PacketRush.Core;

namespace PacketRush.Services.UserManagement
{
    public class ServerNetUserService : NetUserService, INetService
    {
        private PacketWriter _PacketWriter = new PacketWriter();

        private Queue<Packet> _OutgoingPackets = new Queue<Packet>(16);

        public ServerNetUserService(NetServer netServer)
        {
            netServer.Connected += new EventHandler<NetEventArgs>(OnConnected);
            netServer.Disconnected += new EventHandler<NetEventArgs>(OnDisconnected);
            netServer.BeforeUpdate += new EventHandler(OnBeforeUpdate);
        }

        private void OnBeforeUpdate(object sender, EventArgs e)
        {
            // throw new NotImplementedException();
        }

        public void ChangeName(int id, string name)
        {
            if (Users.ContainsKey(id))
            {
                Users[id].Name = name;

                // Send the new name packet to all
                _PacketWriter.Reset();
                _PacketWriter.Write(1); // Only one client in this packet
                _PacketWriter.Write((short)id);
                _PacketWriter.Write(name, Encoding.UTF8);

                _OutgoingPackets.Enqueue(_PacketWriter.ToPacket(PacketFlags.Reliable));

            }
        }

        private void OnConnected(object sender, NetEventArgs e)
        {
            int recipientId = e.ConnectionId;

            //
            // Lets send this player a list of all players connected
            //
            if (Users.Count > 0)
            {
                SendUserList(recipientId);
            }

            //
            // Add the new connected player to the list
            //
            Users.Add(recipientId, new NetUser() { ConnectionId = recipientId, Name = "<unknown>" });
            OnUserAdded(recipientId);
        }

        private void OnDisconnected(object sender, NetEventArgs e)
        {
            if (Users.ContainsKey(e.ConnectionId))
            {
                OnUserRemoved(e.ConnectionId);
                Users.Remove(e.ConnectionId);
            }
        }

        public void DeliverPacket(Packet packet)
        {
            // We are expecting only 'set user name' packets
            // so process delivered packets making this assumption
            string name = ProcessSetUserNamePacket(packet);

            //
            // Let everyone else know about this users name
            //
            SendNewNamePacket(packet.SenderId, name);
        }

        private void SendNewNamePacket(int senderId, string name)
        {
            _PacketWriter.Reset();
            _PacketWriter.Write(1); // Only one client in this packet
            _PacketWriter.Write((short)senderId);
            _PacketWriter.Write(name, Encoding.UTF8);

            _OutgoingPackets.Enqueue(_PacketWriter.ToPacket(senderId, PacketFlags.Reliable, true));
        }

        private string ProcessSetUserNamePacket(Packet packet)
        {
            PacketReader reader = new PacketReader(packet);
            string newName = reader.ReadString(Encoding.UTF8);
            string oldName = Users[packet.SenderId].Name;
            Users[packet.SenderId].Name = newName;
            if (oldName != newName) OnUserChangedName(packet.SenderId, oldName, newName);
            return newName;
        }

        public Packet CollectPacket()
        {
            if (_OutgoingPackets.Count > 0)
            {
                return _OutgoingPackets.Dequeue();
            }

            return null;
        }

        private void SendUserList(int recipientId)
        {
            _PacketWriter.Reset();
            _PacketWriter.Write(Users.Count);
            foreach (NetUser netUser in Users.Values)
            {
                _PacketWriter.Write((short)netUser.ConnectionId);
                _PacketWriter.Write(netUser.Name, Encoding.UTF8);
            }

            _OutgoingPackets.Enqueue(_PacketWriter.ToPacket(recipientId, PacketFlags.Reliable));
        }

    }
}
